﻿using System.Collections.Generic;
using Castle.DynamicProxy;
using Moki.Interfaces;
using Moki.State;

namespace Moki.Core
{
    /// <summary>
    /// Represents interceptor for one mock object. Intercepts method invocation and resolves it by call manager.
    /// </summary>
    internal class MockInterceptor : IInterceptor, IUpdateState
    {
        private CallManager _callManager;

        public MockInterceptor(CallConstraints constraints, MockRepository repository)
            : this(constraints, false, repository)
        {
        }

        public MockInterceptor(CallConstraints constraints, bool interceptsPartialMock, MockRepository repository)
        {
            _callManager = new CallManager();
            _callManager.InterceptsPartialMock = interceptsPartialMock;
            _callManager.CallConstraints = constraints;
            _callManager.Repository = repository;
        }

        event UpdateStateHandler IUpdateState.UpdateState
        {
            add
            {
                UpdateState += value;
            }

            remove
            {
                UpdateState -= value;
            }
        }

        public event UpdateStateHandler UpdateState;

        /// <summary>
        /// List of all stubs
        /// </summary>
        public List<StubContext> Stubs
        {
            get
            {
                return this._callManager.Stubs;
            }
        }

        /// <summary>
        /// List of all invoked calls
        /// </summary>
        public List<CallInvocation> Calls
        {
            get
            {
                return this._callManager.Calls;
            }
        }

        /// <summary>
        /// Whether proxy object is concrete class (partial mock).
        /// </summary>
        public bool InterceptsPartialMock
        {
            get
            {
                return _callManager.InterceptsPartialMock;
            }

            set
            {
                _callManager.InterceptsPartialMock = value;
            }
        }

        public StateChangedNotifier StateNotifier { get; set; }

        public MockingState State { get; set; }
        public object StateArgs { get; set; }

        internal void AddStub(StubContext stub)
        {
            _callManager.AddStub(stub);
        }

        public void Intercept(IInvocation invocation)
        {
            UpdateState(this);

            switch (State)
            {
                case MockingState.Acting:

                    _callManager.ResolveCall(invocation);
                    break;
                case MockingState.Stubbing:
                    _callManager.ResolveStub(invocation);
                    break;
                case MockingState.Verifing:
                    _callManager.ResolveVerify(invocation);
                    break;
                case MockingState.RaisingEvent:
                    var eventToRaise = this._callManager.GetEventToRaise(invocation);
                    StateNotifier.NotifyStateChanged(MockingState.Acting);
                    eventToRaise.DynamicInvoke((object[])StateArgs);
                    break;
                default:
                    break;
            }
        }
    }
}
